#pragma once

#include <string>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include "LockGuard.hpp"

namespace LogMoudle
{
#define SCREEN_TYPE 1
#define FILE_TYPE 2

    // 日志的等级类型
    enum
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string g_file = "./logtxt";                     // 默认的日志文件
    pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; // 全局的互斥锁

    // level pid filename num  date  msgs
    struct log_message
    {
        std::string _log_level;
        pid_t _id;
        std::string _file_name;
        int _line;
        std::string _cur_time;
        std::string _log_msg;
    };

    class Log
    {
        // 将日志等级转换为对应的字符串
        std::string levelToString(int level)
        {
            switch (level)
            {
            case DEBUG:
                return "DEBUG";
            case INFO:
                return "INFO";
            case WARNING:
                return "WARNING";
            case ERROR:
                return "ERROR";
            case FATAL:
                return "FATAL";
            default:
                return "UNKNOWN";
            }
        }

        // 获取当前的时间
        std::string getCurrTime()
        {
            time_t now = time(nullptr);
            struct tm *t = localtime(&now);
            char buffer[128];
            snprintf(buffer, sizeof(buffer), "%d-%02d-%d %02d:%02d:%d",
                     t->tm_year + 1900,
                     t->tm_mon + 1,
                     t->tm_mday,
                     t->tm_hour,
                     t->tm_min,
                     t->tm_sec);

            return buffer;
        }

        // 向显示器刷新日志
        void FlushLogToScreen(const log_message &lg)
        {
            printf("[%s][%d][%s][%d][%s] %s",
                   lg._log_level.c_str(),
                   lg._id,
                   lg._file_name.c_str(),
                   lg._line,
                   lg._cur_time.c_str(),
                   lg._log_msg.c_str());
        }
        // 向文件刷新日志
        void FlushLogToFile(const log_message &lg)
        {
            std::ofstream out(_lgfile, std::ios::app);
            if (!out.is_open())
                return;
            char buffer[2048];
            snprintf(buffer, sizeof(buffer), "[%s][%d][%s][%d][%s] %s",
                     lg._log_level.c_str(),
                     lg._id,
                     lg._file_name.c_str(),
                     lg._line,
                     lg._cur_time.c_str(),
                     lg._log_msg.c_str());
            out << buffer;
            out.close();
        }

        // 刷新日志
        void FlushLog(const log_message &lg)
        {
            LockGuard lockguard(&g_mutex);
            switch (_type)
            {
            case SCREEN_TYPE:
                FlushLogToScreen(lg);
                break;
            case FILE_TYPE:
                FlushLogToFile(lg);
                break;
            }
        }

    public:
        Log(std::string &file = g_file)
            : _type(SCREEN_TYPE), _lgfile(file)
        {
        }

        ~Log()
        {
        }

        void LogMessage(int level, const std::string &filename, int fileline, const char *format, ...)
        {
            log_message lg;
            lg._log_level = levelToString(level);
            lg._id = getpid();
            lg._file_name = filename;
            lg._line = fileline;
            lg._cur_time = getCurrTime();

            // 处理可变参数
            va_list ap;
            va_start(ap, format);
            char buffer[2048];
            vsnprintf(buffer, sizeof(buffer), format, ap);
            lg._log_msg = buffer;
            va_end(ap);

            // 刷新日志
            FlushLog(lg);
        }

        // 刷新日志的位置
        void Enable(int type)
        {
            _type = type;
        }

    private:
        int _type;           // 日志写入类型
        std::string _lgfile; // 日志文件名
    };

    Log log;

#define LOG(Level, Format, ...)                                         \
    do                                                                  \
    {                                                                   \
        log.LogMessage(Level, __FILE__, __LINE__, Format, ##__VA_ARGS__/*这里加 ## 可以当后面可变参数没有时把前面的 , 去掉 */); \
    } while (0)

#define ENABLE_SCREEN()          \
    do                           \
    {                            \
        log.Enable(SCREEN_TYPE); \
    } while (0)

#define ENABLE_FILE()          \
    do                         \
    {                          \
        log.Enable(FILE_TYPE); \
    } while(0)
};// namespace Moudle
